<!-- 使用方法
<m-dialog v-model:visible="_d.visible" 
  	title="产品信息编辑"
  	width="800px"
  	:okLoading="_d.loading"
  	@ok="onSubmit"
  	@closed="() => resetFields()">
</m-dialog>
-->
<template>
<teleport to="body" :disabled="!props.appendToBody">
	<div v-if="_d.show1" ref="mDialogWrapperRef"
		class="m-dialog-wrapper" 
		:class="{
			show: _d.show3,
			'is-drag': props.draggable
		}"
		:style="{
			display: _d.show2 ? 'block' : 'none',
			zIndex: _d.zIndex
		}"
		@click="maskClick()">
	  	<div class="m-dialog-modal">
	  		<div class="m-dialog-cell">
	  			<div class="m-dialog-container"
	  				:style="{
	  					width: _d.width, 
	  					maxWidth: _d.maxWidth,
	  					transform: `translate(${_d.drag.resultX}px, ${_d.drag.resultY}px)`,
	  				}">
	  				<div class="m-dialog"
		  				ref="mDialogRef"
		  				@click.stop>
				  		<div class="m-dialog-title"
				  			@mousedown="dragDown">
				  			<template v-if="title">{{title}}</template>
				  			<slot v-else name="title"></slot>
				  			<div class="m-dialog-close" @click="handleCancel()">
				  				<svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg>
				  			</div>
				  		</div>
					  	<div class="m-dialog-content" :style="bodyStyle">
					  		<slot></slot>
					  	</div>
					  	<div class="m-dialog-footer">
					  		<button v-if="props.showCancel" class="btn" @click="handleCancel()">{{cancelText}}</button>
					  		<button v-if="props.showOk" :loading="props.okLoading" class="btn" type="primary" @click="handleOk()">{{okText}}</button>
					  	</div>
				  	</div>
	  			</div>
	  		</div>
	  	</div>
	</div>
</teleport>
</template>

<script setup>
	import { ref, reactive, watch, onBeforeMount, onBeforeUnmount } from 'vue'

  const mDialogWrapperRef = ref();
  const mDialogRef = ref();

	const emit = defineEmits(['update:visible', 'ok', 'cancel', 'closed'])

	const props = defineProps({
		visible: {
			type: Boolean,
			default: false
		},
		width: {
			type: String,
			default: ''
		},
		maxWidth: {
			type: String,
			default: ''
		},
		title: {
			type: String,
			default: ''
		},
		footer: {
			type: Boolean,
			default: true
		},
		showOk: {
			type: Boolean,
			default: true
		},
		showCancel: {
			type: Boolean,
			default: true
		},
		okText: {
			type: String,
			default: '确认修改'
		},
		cancelText: {
			type: String,
			default: '暂不修改'
		},
		// 确认按钮-加载中
		okLoading: {
			type: Boolean,
			default: false
		},
		// 点击蒙层是否允许关闭
		maskClosable: {
			type: Boolean,
			default: true
		},
		// 内容样式
		bodyStyle: {
			type: Object,
			default: () => ({})
		},
		// 层级 - 如果设置了就是固定层级，如果不设置从最多层级1000开始往上加
		zIndex: {
			type: Number,
			default: 0
		},
		// 是否加到body节点下
		appendToBody: {
			type: Boolean,
			default: true
		},
		// 当关闭 Dialog 时，销毁其中的元素
		destroyOnClose: {
			type: Boolean,
			default: true
		},
		// 为 Dialog 启用可拖拽功能
		draggable: {
			type: Boolean,
			default: false
		}
	})

	const _d = reactive({
		show1: false, // 控制v-if
		show2: false, // 控制display
		show3: false, // 控制动画
		zIndex: 1000, // 最低层级
		width: !props.width && !props.maxWidth ? '400px' : props.width, // 宽度
		maxWidth: props.maxWidth, // 最大宽度

		// 拖动参数
		drag: {
			// 是否点下
			isDown: false,
			// 整个页面你最大宽高
			pageWidth: 0,
			pageHeight: 0,
			// 弹窗宽高
			dialogWidth: 0,
			dialogHeight: 0,
			// 左右最大移动距离
			minX: 0,
			maxX: 0,
			minY: 0,
			maxY: 0,
			// 移动结果
			resultX: 0,
			resultY: 0,
			// 鼠标点下是历史移动距离
			startX: 0,
			startY: 0,
			// 鼠标点下时相对于page的位置
			downX: 0,
			downY: 0,
			// 鼠标移动位置
			moveX: 0,
			moveY: 0,
		}
	})

	watch(() => props.visible, (newVal) => {
		setVisible();
	})

	onBeforeMount(() => {
		setVisible();
		// 绑定鼠标事件
		window.addEventListener('mousemove', dragMove);
		window.addEventListener('mouseup', dragUp);
	})

	onBeforeUnmount(() => {
		// 如果appendToBody属性为true, 移除掉插入到body上面的弹框组件
		let $el = mDialogWrapperRef.value;
		if (props.appendToBody && $el && $el.parentNode) {
			$el.parentNode.removeChild($el);
		}
		// 解绑鼠标事件
		window.removeEventListener('mousemove', dragMove);
		window.removeEventListener('mouseup', dragUp);
	})

	// 设置显示状态
	function setVisible () {
		if(props.visible) {
			new Promise((resolve, reject) => {
				if(!_d.show1) { // 第一次通过v-if添加节点到dom树
					setZIndex();
					_d.show1 = true;
					_d.show2 = true;
					setTimeout(() => {
						resolve();
					}, 30);
				} else { // 关闭后不删除节点，再显示弹窗
					_d.show2 = true;
					setTimeout(() => {
						resolve();
					}, 30);
				}
			}).then(() => {
				document.body.style.overflow = "hidden";
				_d.show3 = true;
			})
		} else { // 关闭弹窗
			_d.show3 = false; // 关闭时动画效果
			setTimeout(() => {
				_d.show2 = false; // 设置display:none
				if(props.destroyOnClose) {
					_d.show1 = false; // 通过v-if销毁节点
				}
				document.body.style.overflow = "";
				emit('closed');
			}, 250)
		}
	}

	// 显示的时候设置z-index
	function setZIndex() {
		let max = 1000;
		// 判断是否有自定义的层级
		if(props.zIndex) {
			max = props.zIndex;
		} else {
			let aMd = [...document.querySelectorAll('.m-dialog.show')];
			aMd.forEach(obj => {
				let num = getStyle(obj, 'z-index');
				if(max <= num) {
					max = num+1;
				}
			})
		}
		_d.zIndex = max;
	}

	// 获取样式
	function getStyle (obj, name) {
	    if(window.getComputedStyle) {
	        return getComputedStyle(obj, null)[name];
	    } else {
	        return obj.currentStyle[name];
	    }
	}

	// 确认
	function handleOk() {
		emit('ok');
	}

	// 取消
	function handleCancel() {
		emit('update:visible', false);
		emit('cancel');
	}

	// 蒙层点击
	function maskClick () {
		if(props.maskClosable) {
			emit('update:visible', false);
		}
	}

	// 拖动 ----------------------------------------------------
	function dragDown(e) {
		if(!props.draggable) {
			return;
		}
		// 是否按下
		_d.drag.isDown = true;
		// 页面宽高
		_d.drag.pageWidth = mDialogWrapperRef.value.clientWidth;
		_d.drag.pageHeight = mDialogWrapperRef.value.clientHeight;
		// 弹窗宽高
		_d.drag.dialogWidth = mDialogRef.value.clientWidth;
		_d.drag.dialogHeight = mDialogRef.value.clientHeight;

		// 获取弹窗到页面左上角的距离
		let dis = getDistanceBody(mDialogRef.value);
		// 计算边界
		let minX = -dis.left;
		let minY = -dis.top;
		let maxX = _d.drag.pageWidth - _d.drag.dialogWidth - dis.left;
		let maxY = _d.drag.pageHeight - _d.drag.dialogHeight - dis.top;
		if(minX > 0) {
			minX = 0;
		}
		if(minY > 0) {
			minY = 0;
		}
		if(maxX > _d.drag.pageWidth - _d.drag.dialogWidth) {
			maxX = _d.drag.pageWidth - _d.drag.dialogWidth;
		}
		if(maxY > _d.drag.pageHeight - _d.drag.dialogHeight) {
			maxY = _d.drag.pageHeight - _d.drag.dialogHeight;
		}
		_d.drag.minX = minX;
		_d.drag.maxX = maxX;
		_d.drag.minY = minY;
		_d.drag.maxY = maxY-1;// 完成相等会出现滚动条
		// 开始位置
		_d.drag.startX = _d.drag.resultX;
		_d.drag.startY = _d.drag.resultY;
		// 鼠标点下时相对于page的位置
		_d.drag.downX = e.pageX;
		_d.drag.downY = e.pageY;
	}
	function dragMove(e) {
		if(!props.draggable) {
			return;
		}
		if(!_d.drag.isDown) {
			return;
		}
		// 移动距离
		_d.moveX = e.pageX-_d.drag.downX;
		_d.moveY = e.pageY-_d.drag.downY;
		// 涉及结果位置
		let resultX = _d.drag.startX+_d.moveX;
		let resultY = _d.drag.startY+_d.moveY;
		if(resultX < _d.drag.minX) {
			resultX = _d.drag.minX;
		}
		if(resultX > _d.drag.maxX) {
			resultX = _d.drag.maxX;
		}
		if(resultY < _d.drag.minY) {
			resultY = _d.drag.minY;
		}
		if(resultY > _d.drag.maxY) {
			resultY = _d.drag.maxY;
		}
		_d.drag.resultX = resultX;
		_d.drag.resultY = resultY;
	}
	// 鼠标抬起
	function dragUp(e) {
		if(!props.draggable) {
			return;
		}
		if(!_d.drag.isDown) {
			return;
		}
		_d.drag.isDown = false;
	}
	// 获取节点到body的高度
	function getDistanceBody (obj) {
		var oBody = document.body;
		var oHtml = document.documentElement;
		var oParent = null;
		var oDistance = {
			top: 0,
			left: 0
		}
		do {
			oDistance.top += obj.offsetTop;
			oDistance.left += obj.offsetLeft;
			oParent = obj.offsetParent;
			obj = oParent;
		} while(oParent && oParent != oBody && oParent != oHtml)

		return oDistance;
	}


</script>

<style lang="less" scoped>
.m-dialog-wrapper {
	position: fixed;
	z-index: 1000;
	top: 0;
	right: 0;
	left: 0;
	bottom: 0;
	background: rgba(0,0,0,0.25);
	transition: opacity .25s;
	opacity: 0;
	overflow: auto;
	&.show {
		opacity: 1;
		.m-dialog {
			transform: scale(1);
		}
	}
	&.is-drag {
		-moz-user-select:none; /*火狐*/
		-webkit-user-select:none; /*webkit浏览器*/
		-ms-user-select:none; /*IE10*/
		-khtml-user-select:none; /*早期浏览器*/
		user-select:none;
		.m-dialog-title {
			cursor: move;
		}
	}
	.m-dialog-modal {
		display: table;
		padding: 20px 0 40px;
		min-height: 100%;
		min-width: 100%;
		box-sizing: border-box;
	}
	.m-dialog-cell {
		display: table-cell;
		vertical-align: middle;
	}
	.m-dialog-container {
		margin: 0 auto;
	}
	.m-dialog {
		position: relative;
		z-index: 2;
		border-radius: 2px;
		overflow: hidden;
		background: #fff;
		transition: transform .25s;
		transform: scale(0.9);
		box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
		.m-dialog-title {
			position: relative;
			box-sizing: border-box;
			padding: 16px 24px;
			border-bottom: 1px solid #f0f0f0;
			color: #000;
			font-size: 16px;
			line-height: 22px;
			word-wrap: break-word;
			min-height: 55px;
			.m-dialog-close {
				position: absolute;
				top: 50%;
				right: 0;
				margin-top: -27px;
				width: 54px;
				height: 54px;
				display: flex;
				justify-content: center;
				align-items: center;
				font-size: 16px;
				color: #666;
				cursor: pointer;
				&:hover {
					color: #333;
				}
			}
		}
		.m-dialog-content {
			padding: 24px;
			min-height: 100px;
		}
		.m-dialog-footer {
			padding: 10px 16px;
	    text-align: right;
	    background: transparent;
	    border-top: 1px solid #f0f0f0;
	    border-radius: 0 0 2px 2px;
			.btn {
				padding: 4px 15px;
				font-size: 14px;
				color: #333;
				border: 1px solid #d9d9d9;
				background: #fff;
				height: 32px;
				font-weight: 400;
				cursor: pointer;
				outline: medium;
				border-radius: 2px;
				&:not(:last-child) {
					margin-right: 8px;
				}
			}
		}
	}	
}
</style>